﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace System.Collections.Generic
{
	public class CircularQueue<CircularQueueItem>
	{
		#region Private Class Members

		private const int DefaultMillisecondsTimeout = 1000;
		private CircularQueueItem[] Items;
		private int HeadIndex;
		private int TailIndex;
		private int _Length;

		#endregion

		#region Public Class Members

		/// <summary>
		/// 用于多线程同时访问队首元素（调用 Pop / Front 方法）时的锁对象。
		/// </summary>
		public readonly object HeadSyncObject;

		/// <summary>
		/// 用于多线程同时访问队尾元素（调用 Push / Back 方法）时的锁对象。
		/// </summary>
		public readonly object TailSyncObject;

		/// <summary>
		/// 获取队列的最大可用长度，该值通过方法 <see cref="Init"/> 设置。
		/// </summary>
		public int Capacity
		{
			get { return this.Items.Length; }
		}

		/// <summary>
		/// 获取队列的使用长度。
		/// </summary>
		public int Length
		{
			get { return this._Length; }
		}

		#endregion

		#region Class Constructor

		public CircularQueue()
		{
			this.TailSyncObject = new object();
			this.HeadSyncObject = new object();
			this.Items = null;
			this._Length = 0;
		}

		#endregion 

		#region IsFull 获取一个值，该值指示当前队列是否已满。

		/// <summary>
		/// 获取一个值，该值指示当前队列是否已满。
		/// </summary>
		/// <returns></returns>
		public bool IsFull()
		{
			if(this.Items == null)
			{
				throw new InvalidOperationException("Please initialize first.");
			}

			return this._Length == this.Items.Length;
		}

		#endregion

		#region IsEmpty 获取一个值，该值指示当前队列是否为空。

		/// <summary>
		/// 获取一个值，该值指示当前队列是否为空。
		/// </summary>
		/// <returns></returns>
		public bool IsEmpty()
		{
			return this._Length == 0;
		}

		#endregion

		#region Init 初始化

		public delegate bool ItemInitHandler(ref CircularQueueItem item);

		public event ItemInitHandler ItemInit;

		/// <summary>
		/// 初始化。
		/// </summary>
		/// <param name="capacity">队列的最大容量。</param>
		/// <returns></returns>
		public bool Init(int capacity)
		{
			if(capacity <= 1)
			{
				throw new ArgumentOutOfRangeException("capacity", "The capacity should greater than 1.");
			}

			if(this.Items != null)
			{
				throw new InvalidOperationException("The queue is already initialized. UnInit it first.");
			}

			this.Items = new CircularQueueItem[capacity];
			if(this.ItemInit != null)
			{
				for (int index = 0; index < capacity; index++)
				{
					if(!this.ItemInit(ref this.Items[index]))
					{
						return false;
					}
				}
			}

			this.HeadIndex = 0;
			this.TailIndex = capacity - 1;

			return true;
		}

		#endregion

		#region UnInit 销毁

		public delegate void ItemUnInitHandler(ref CircularQueueItem item);

		public event ItemUnInitHandler ItemUnInit;

		public void UnInit()
		{
			if (this.ItemUnInit != null)
			{
				int capacity = this.Items.Length;
				for (int index = 0; index < capacity; index++)
				{
					this.ItemInit(ref this.Items[index]);
				}
			}
			this.Items = null;
		}

		#endregion

		#region Front 获取但不删除队首的元素。

		/// <summary>
		/// 获取但不删除队首的元素。
		/// </summary>
		/// <param name="item"></param>
		/// <returns>如果当前队列非空，则返回 true ，否则返回 false 。</returns>
		public bool Front(ref CircularQueueItem item)
		{
			if (this.IsEmpty())
			{
				return false;
			}

			item = this.Items[this.HeadIndex];

			return true;
		}

		#endregion

		#region Back 获取但不删除队尾的元素。

		/// <summary>
		/// 获取但不删除队尾的元素。
		/// </summary>
		/// <param name="item"></param>
		/// <returns>如果当前队列非空，则返回 true ，否则返回 false 。</returns>
		public bool Back(ref CircularQueueItem item)
		{
			if (this.IsEmpty())
			{
				return false;
			}

			item = this.Items[this.TailIndex];

			return true;
		}

		#endregion

		#region Push 在队尾放入一个新元素。

		/// <summary>
		/// 调用 Push 方法时的委托。
		/// </summary>
		/// <param name="item">队列元素的引用。</param>
		/// <param name="callbackArgs">调用 Push 方法时传递的参数。</param>
		/// <returns></returns>
		public delegate bool ItemPushHandler(ref CircularQueueItem item, object callbackArgs);

		/// <summary>
		/// 在队尾放入一个新元素。队列满时不等待。
		/// </summary>
		/// <param name="callback">Push 时的回调函数。</param>
		/// <param name="callbackArgs">回调函数 callback 将要用到的参数。</param>
		/// <returns>如果队列未满，且 callback 调用成功（返回 true），则返回 true；否则返回 false 。</returns>
		public bool Push(ItemPushHandler callback, object callbackArgs)
		{
			return this.Push(callback, callbackArgs, false, 0);
		}

		/// <summary>
		/// 在队尾放入一个新元素。
		/// </summary>
		/// <param name="callback">Push 时的回调函数。</param>
		/// <param name="callbackArgs">回调函数 callback 将要用到的参数。</param>
		/// <param name="waitIfFull">如果队列已满，是否等待。</param>
		/// <returns>如果队列未满，且 callback 调用成功（返回 true），则返回 true；否则返回 false 。</returns>
		public bool Push(ItemPushHandler callback, object callbackArgs, bool waitIfFull)
		{
			return this.Push(callback, callbackArgs, waitIfFull, DefaultMillisecondsTimeout);
		}

		/// <summary>
		/// 在队尾放入一个新元素。
		/// </summary>
		/// <param name="callback">Push 时的回调函数。</param>
		/// <param name="callbackArgs">回调函数 callback 将要用到的参数。</param>
		/// <param name="waitIfFull">如果队列已满，是否等待。</param>
		/// <param name="millisecondsTimeout">等待时长（毫秒）。</param>
		/// <returns>如果队列未满，且 callback 调用成功（返回 true），则返回 true；否则返回 false 。</returns>
		public bool Push(ItemPushHandler callback, object callbackArgs, bool waitIfFull, int millisecondsTimeout)
		{
			if(this.IsFull())
			{
				if(!waitIfFull)
				{
					return false;
				}
				else
				{
					DateTime end = DateTime.Now.AddMilliseconds(millisecondsTimeout);
					while(DateTime.Now < end)
					{
						if(this.IsFull())
						{
							Thread.Sleep(1);
						}
					}
				}
			}

			if (this.IsFull())
			{
				return false;
			}

			int newTailIndex = this.TailIndex == this.Items.Length - 1 ? 0 : this.TailIndex + 1;
			if (callback(ref this.Items[newTailIndex], callbackArgs))
			{
				this.TailIndex = newTailIndex;
				this._Length++;
			}

			return newTailIndex == this.TailIndex;
		}

		#endregion

		#region Pop 获取并删除队首的元素。

		/// <summary>
		/// 获取并删除队首的元素。队列为空时不等待。
		/// </summary>
		/// <param name="item">获取到的元素的引用。</param>
		/// <returns>如果队列非空，则返回 true ，否则返回 false 。</returns>
		public bool Pop(ref CircularQueueItem item)
		{
			return this.Pop(ref item, false, 0);
		}

		/// <summary>
		/// 获取并删除队首的元素。
		/// </summary>
		/// <param name="item">获取到的元素的引用。</param>
		/// <param name="waitIfEmpty">如果队列为空，是否等待。</param>
		/// <returns>如果队列非空，则返回 true ，否则返回 false 。</returns>
		public bool Pop(ref CircularQueueItem item, bool waitIfEmpty)
		{
			return this.Pop(ref item, waitIfEmpty, DefaultMillisecondsTimeout);
		}

		/// <summary>
		/// 获取并删除队首的元素。
		/// </summary>
		/// <param name="item">获取到的元素的引用。</param>
		/// <param name="waitIfEmpty">如果队列为空，是否等待。</param>
		/// <param name="millisecondsTimeout">等待时长（毫秒）。</param>
		/// <returns>如果队列非空，则返回 true ，否则返回 false 。</returns>
		public bool Pop(ref CircularQueueItem item, bool waitIfEmpty, int millisecondsTimeout)
		{
			if (this.IsEmpty())
			{
				if (!waitIfEmpty)
				{
					return false;
				}
				else
				{
					DateTime end = DateTime.Now.AddMilliseconds(millisecondsTimeout);
					while (DateTime.Now < end)
					{
						if (this.IsFull())
						{
							Thread.Sleep(1);
						}
					}
				}
			}

			if (this.IsEmpty())
			{
				return false;
			}

			this.HeadIndex = this.HeadIndex == this.Items.Length - 1 ? 0 : this.HeadIndex + 1;
			this._Length--;

			return true;
		}

		#endregion
	}
}
